package controller

import (
	"backEnd/common"
	"backEnd/dto"
	"backEnd/model"
	"fmt"
	"log"
	"math/rand"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/jinzhu/gorm"
)

func Page(ctx *gin.Context) {
	var (
		Product         model.Product
		ProductsDetail  = make([]model.ProductDetail, 0)
		ProductsDto     = make([]dto.ProductDto, 0)
		ProductCoverImg model.ProductCoverImg
		count           int
		productIDs      []string
	)

	DB := common.GetDB()
	common.InitRedis()

	// 获取客户端标识符
	clientID := getClientID(ctx)

	// 判断是否有refresh
	if ctx.Query("refresh") == "true" {
		if err := common.GetCache().Del(fmt.Sprintf("client:%s:productIDs", clientID)).Err(); err != nil {
			ctx.JSON(500, gin.H{
				"msg": "无法清空缓存",
				"err": err,
			})
			return
		}
	}

	// 从缓存中获取已经返回过的商品ID
	cacheKey := fmt.Sprintf("client:%s:productIDs", clientID)
	cachedProductIDs, err := common.GetCache().SMembers(cacheKey).Result()
	if err != nil {
		ctx.JSON(500, gin.H{
			"msg": "无法获取缓存",
			"err": err,
		})
		return
	}

	log.Printf("缓存命中情况: %v", cachedProductIDs)

	// 判断缓存是否过期，如果过期则清空缓存
	if ttl, err := common.GetCache().TTL(cacheKey).Result(); err == nil && ttl < 0 {
		if err := common.GetCache().Del(cacheKey).Err(); err != nil {
			ctx.JSON(500, gin.H{
				"msg": "无法清空缓存",
				"err": err,
			})
			return
		}
		cachedProductIDs = []string{}
	}

	// 取出所有未销售、未过期商品的id并拿到总数据量
	if err := DB.Model(&model.ProductDetail{}).Where("sell_status = ? AND expirated_at > ?", 0, time.Now()).Pluck("id", &productIDs).Count(&count).Error; err != nil {
		ctx.JSON(500, gin.H{
			"msg": "无法查询到商品",
			"err": err,
		})
		return
	}

	fmt.Print(productIDs)

	//去除掉未销售商品中已经在缓存中出现过的
	returnProductIDs := subtractArray(productIDs, cachedProductIDs)
	//优化至随机取10个
	returnProductIDs = randomStrings(returnProductIDs)

	// 将本次返回的商品ID添加到缓存中，并设置缓存有效期为5分钟
	if err := common.GetCache().SAdd(cacheKey, returnProductIDs).Err(); err != nil {
		ctx.JSON(500, gin.H{
			"msg": "无法更新缓存",
			"err": err,
		})
		return
	}
	if err := common.GetCache().Expire(cacheKey, time.Minute*5).Err(); err != nil {
		ctx.JSON(500, gin.H{
			"msg": "无法设置缓存有效期",
			"err": err,
		})
		return
	}

	//依照productID获取到对应商品记录
	if err := DB.Where("id IN (?)", returnProductIDs).Find(&ProductsDetail).Error; err != nil {
		ctx.JSON(404, gin.H{
			"msg": "无法查询到商品",
			"err": err,
		})
		return
	}

	//依据detail查询到product
	for _, ProductDetail := range ProductsDetail {
		if err := DB.Where("product_id = ?", ProductDetail.ID).Find(&Product).Error; err != nil {
			ctx.JSON(500, gin.H{"msg": "商品查询失败"})
			return
		}
		ProductsDto = append(ProductsDto, dto.ToProductDto(Product))
		Product = model.Product{}
	}

	//填充封面图片url
	for i := range ProductsDto {
		if err := DB.Where("product_id = ?", ProductsDto[i].ProductId).First(&ProductCoverImg).Error; err != nil {
			if err == gorm.ErrRecordNotFound {
				// 记录不存在，填充空值
				ProductCoverImg = model.ProductCoverImg{ProductCoverImg: ""}
			} else {
				fmt.Print("封面图片填充失败,出错商品id:", ProductsDto[i].ProductId)
				ctx.JSON(500, gin.H{
					"msg": "封面图片填充失败"})
				return
			}
		}
		ProductsDto[i].ProductCoverImg = make([]dto.CoverImg, 1)
		ProductsDto[i].ProductCoverImg[0].ProductCoverImg = ProductCoverImg.ProductCoverImg
	}

	ctx.JSON(200, gin.H{
		"data": ProductsDto,
	})
}

// 获取客户端标识符
func getClientID(ctx *gin.Context) string {
	// 优先使用X-Forwarded-For头部中的IP地址
	if xff := ctx.Request.Header.Get("X-Forwarded-For"); xff != "" {
		return strings.Split(xff, ",")[0]
	}
	// 如果X-Forwarded-For头部中没有IP地址，则使用RemoteAddr
	return ctx.Request.RemoteAddr
}

// 数组比较函数
func subtractArray(a, b []string) []string {
	// 使用map记录b中出现过的数字
	bMap := make(map[string]bool)
	for _, num := range b {
		bMap[num] = true
	}

	// 遍历a数组，将不在b中出现的数字加入结果数组
	var res []string
	for _, num := range a {
		if !bMap[num] {
			res = append(res, num)
		}
	}

	return res
}

func randomStrings(strs []string) []string {
	// 随机种子
	rand.Seed(time.Now().UnixNano())

	n := len(strs)
	if n <= 10 {
		// 如果元素数量不足10个，则直接返回全部元素
		return strs
	}

	// 随机选择10个元素
	randIndexes := rand.Perm(n)
	res := make([]string, 10)
	for i := 0; i < 10; i++ {
		res[i] = strs[randIndexes[i]]
	}
	return res
}
